package com.z.common.pro.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.z.common.aop.annotation.Log;
import com.z.common.aop.enums.BusinessType;
import com.z.common.context.UserContext;
import com.z.common.gen.entity.SysDictData;
import com.z.common.gen.service.ISysDictDataService;
import com.z.common.pro.vo.ActivitiHighLineVo;
import com.z.common.pro.vo.ProDefined;
import com.z.common.web.base.BaseController;
import com.z.common.web.vo.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.VariableMap;
import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@RestController
@RequestMapping("process")
@Slf4j
public class CamundaController extends BaseController {

    private final String prefix = "system/process/";

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ISysDictDataService sysDictDataService;

    @Autowired
    private IdentityService identityService;
    /**
     * 我的流程页面
     * @param model
     * @return
     */
    @GetMapping("main")
    public ModelAndView main(Model model) {

        String url = "/process/backlog";
        model.addAttribute("url", url);
        model.addAttribute("src2", "/process/track");
        model.addAttribute("src3", "/process/history");
        return jumpPage(prefix + "main");
    }

    /**
     * 我的流程页面
     * @return
     */
    @GetMapping("backlog")
    public ModelAndView backlog() {
        return jumpPage(prefix + "backlog");
    }

    /**
     * 我的流程页面
     * @return
     */
    @GetMapping("track")
    public ModelAndView track() {
        return jumpPage( prefix + "track");
    }

    /**
     * 我的流程页面
     * @return
     */
    @GetMapping("history")
    public ModelAndView history() {
        return jumpPage(prefix + "history");
    }


    @PostMapping("start")
    @ResponseBody
    @Log(title = "启动流程", describe = "根据key启动流程", type = BusinessType.ADD)
    public Result start(@RequestBody ProDefined defined){
        String key = defined.getKey();
        if(StringUtils.isNotEmpty(key)){
            try{
                Long userId = UserContext.currentUser().getId();
                identityService.setAuthenticatedUserId(userId+"");
                final VariableMap variables = Variables.createVariables();
                variables.putValue("initiator", userId+"");
                ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);  //画流程图时设置的process_id
                Task task = taskService.createTaskQuery()
                        .processInstanceId(processInstance.getId())
                        .singleResult();
                String taskId = task.getId();
                taskService.setAssignee(taskId, userId + "");
                if(processInstance != null){
                    log.info("流程发起成功");
                }else{
                    log.info("流程发起失败");
                }

                // 查询页面地址
                SysDictData data = sysDictDataService.getOne(new QueryWrapper<SysDictData>()
                        .select("data_value")
                        .eq("type_code", "process_start_url").eq("is_enable", 1)
                        .eq("data_label", key));
                return Result.success(1, data.getDataValue() + processInstance.getId());
            }catch (Exception e){
                return Result.error(e.getMessage());
            }

        }else {
            return Result.error("参数异常");
        }

    }

    @PostMapping("delete")
    @ResponseBody
    public Result delete(String taskId){
        try{
            Task task = taskService.createTaskQuery().taskId(taskId).initializeFormKeys().singleResult();
            String id = task.getProcessInstanceId();
            runtimeService.deleteProcessInstance(id, "手动删除");
        } catch (Exception e){
            return Result.error(e.getMessage());
        }
        return Result.success();
    }

    @PostMapping("deleteByInsId")
    @ResponseBody
    public Result deleteByInsId(String instanceId){
        try{
            runtimeService.deleteProcessInstance(instanceId, "手动删除");
        } catch (Exception e){
            return Result.error(e.getMessage());
        }
        return Result.success();
    }

    /**
     * 流程图查看页面
     * @return
     */
    @GetMapping("/getXml")
    public void getXml(String processInsId, HttpServletResponse response) {
        int result = 0;
        try {
            HistoricProcessInstance hisProIns = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInsId).singleResult();

            ProcessDefinition processDefinition = repositoryService.getProcessDefinition(hisProIns.getProcessDefinitionId());
            InputStream b = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
            BufferedInputStream bis = new BufferedInputStream(b);
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            result = bis.read();
            while(result != -1) {
                buf.write((byte) result);
                result = bis.read();
            }
            bis.close();
            String str = buf.toString();
            response.setCharacterEncoding("UTF-8");
            response.getWriter().print(str);
            response.getWriter().flush();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 流程图查看页面
     * @return
     */
    @GetMapping("/viewBpmn")
    public ModelAndView roleSelectMain(Model model, String id) {
        model.addAttribute("id", id);
        return jumpPage(prefix + "bpmnViewMain");
    }
    @GetMapping("/getHighlightNode")
    @ResponseBody
    public ActivitiHighLineVo getHighlightNode(String processInsId) {
        HistoricProcessInstance hisProIns = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInsId).singleResult();
        //System.out.println(hisProIns.getProcessDefinitionName()+" "+hisProIns.getProcessDefinitionKey());
        //===================已完成节点
        List<HistoricActivityInstance> finished = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        Set<String> highPoint = new HashSet<>();
        finished.forEach(t -> highPoint.add(t.getActivityId()));

        //=================待完成节点
        List<HistoricActivityInstance> unfinished = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInsId).unfinished().list();
        Set<String> waitingToDo = new HashSet<>();
        unfinished.forEach(t -> waitingToDo.add(t.getActivityId()));

        //=================iDo 我执行过的
        Set<String> iDo = new HashSet<>(); //存放 高亮 我的办理节点
        List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery().taskAssignee(UserContext.currentUser().getUsername()).finished().processInstanceId(processInsId).list();
        taskInstanceList.forEach(a -> iDo.add(a.getTaskDefinitionKey()));

        //===========高亮线
        Set<String> highLine2 = new HashSet<>(); //保存高亮的连线
        //获取流程定义的bpmn模型
        BpmnModelInstance bpmn = repositoryService.getBpmnModelInstance(hisProIns.getProcessDefinitionId());
        //已完成任务列表 可直接使用上面写过的
        List<HistoricActivityInstance> finishedList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        int finishedNum = finishedList.size();
        //循环 已完成的节点
        for (int i = 0; i < finishedNum; i++) {
            HistoricActivityInstance finItem = finishedList.get(i);
            //根据 任务key 获取 bpmn元素
            ModelElementInstance domElement = bpmn.getModelElementById(finItem.getActivityId());
            //转换成 flowNode流程节点 才能获取到 输出线 和输入线
            FlowNode act = (FlowNode)domElement;
            Collection<SequenceFlow> outgoing = act.getOutgoing();
            //循环当前节点的 向下分支
            outgoing.forEach(v->{
                String tarId = v.getTarget().getId();
                //已完成
                for (int j = 0; j < finishedNum; j++) {
                    //循环历史完成节点 和当前完成节点的向下分支比对
                    //如果当前完成任务 的结束时间 等于 下个任务的开始时间
                    HistoricActivityInstance setpFinish = finishedList.get(j);
                    String finxId = setpFinish.getActivityId();
                    if(tarId.equals(finxId)){
                        if(finItem.getEndTime().equals(setpFinish.getStartTime())){
                            highLine2.add(v.getId());
                        }
                    }
                }
                //待完成
                for (int j = 0; j < unfinished.size(); j++) {
                    //循环待节点 和当前完成节点的向下分支比对
                    HistoricActivityInstance setpUnFinish = unfinished.get(j);
                    String finxId = setpUnFinish.getActivityId();
                    if(tarId.equals(finxId)){
                        if(finItem.getEndTime().equals(setpUnFinish.getStartTime())){
                            highLine2.add(v.getId());
                        }
                    }
                }

            });
        }

        //返回结果
        ActivitiHighLineVo activitiHighLineDTO =new ActivitiHighLineVo();
        activitiHighLineDTO.setHighPoint(highPoint);
        activitiHighLineDTO.setHighLine(highLine2);
        activitiHighLineDTO.setWaitingToDo(waitingToDo);
        activitiHighLineDTO.setIDo(iDo);
        return activitiHighLineDTO;
    }

}
